/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package hilos;

import IOFPGA.EntradaSalida;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JTextArea;
import interfaz.GUIPrincipal;

/**
 * Hilo que se crea durante todo el proceso de reconfiguración
 * @author Felipe Serrano y Víctor Alaminos
 */

public class HiloInyeccion extends HiloNessy{
    private JTextArea panel;
    private EntradaSalida entsal;
    private int tam_bit;
    private String tam32str;
    
    public static final int NUM_WORDS_FRAME = 41;
    public static final int NUM_BITS_WORD = 32;
    public static final int[] minor_per_column = {54,36,36,36,36,30,36,36,36,36,36,36,36,36,36,36,30,36,36,28,36,36,36,36,36,36,36,36,54,4,
                                 36,36,36,36,36,36,36,36,36,36,36,36,30,36,36,36,36,36,36,36,36,36,36,30,36,36,36,36,54,36,36,36,36,30,32};

    public HiloInyeccion(String nombre, JTextArea panel, EntradaSalida entsal){
        setName(nombre);
        this.panel = panel;
        this.entsal = entsal;
        this.tam_bit = GUIPrincipal.tamaño32bits(GUIPrincipal.fichero_bit_parcial);
        this.tam32str = Integer.toBinaryString(tam_bit);
    }

    @Override
    public void run(){
        if(!GUIPrincipal.parcialEnviado) enviarBitstream();
        inyectarErrores();
    }


    
    /**
     * Envia el bitstream parcial a la FPGA por el puerto serie
     */
    public void enviarBitstream(){
        BufferedInputStream inputStream = null;
        int enviados=0, byteleido=0;
        String cad = "";
        String texto = "";
        String cabecera = "Enviando bitstream original a la RAM... ";
        String cola = " palabras de 32bits enviadas";
        String palabrasEnviadas;
        
        try {
            //Enviar el comando de recibir bitstream parcial junto con el tamaño
            entsal.enviarBinaria(7);
            entsal.enviarBinaria(tam32str);
            
            //Enviar el bitstream parcial por el puerto serie
            inputStream = new BufferedInputStream(new FileInputStream(GUIPrincipal.fichero_bit_parcial));
            byteleido = inputStream.read();
            while (byteleido != -1) {
                enviados++;
                cad = cad + GUIPrincipal.intToBinario8(byteleido);
                if (enviados % 4 == 0) {
                    entsal.enviarBinaria(cad);
                    palabrasEnviadas = "("+enviados/4+"/"+tam_bit+")";
                    texto = cabecera + palabrasEnviadas + cola;
                    panel.setText(texto);
                    cad = "";
                }
                byteleido = inputStream.read();
            }
            // Envía la última palabra, que lo más seguro es que esté incompleta (<32bits)
            if (!cad.equals("")) {
                entsal.enviarBinaria(cad);
            }
            //Cerrar ficheros y buffers.
            inputStream.close();
            String envioBitstream = cabecera + "Ok\n";
            texto = envioBitstream;
            panel.setText(texto);
            panel.append("**********************************************\n");
        } catch (IOException ex) {
            Logger.getLogger(HiloInyeccion.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("Error enviando bitstream parcial");
        }
    }



    /**
     * Reconfigura la región del circuito mapeada en la FPGA tantas veces como bitflips
     * existan ejecutando para cada uno el testbench
     */
    public void inyectarErrores() {
        int cicloError, status;
        String datoError, dato_Golden;
        int num_Words;
        double numeroConf, numeroConfErroneas, numeroConfErroneasEstaticas;
        long tiempoInicial, tiempoFinal, tiempoInicial2 = 0, tiempoFinal2 , tiempoTotal2 = 0;
        int noErrores = 0;
        int num_FDRI_blocks, num_FDRI_block_address, major, major_address, max_major, minor, minor_address, max_minor, word, word_address, num_word, bit, bit_address = 0;
        String data_Str;
        String comando_reconfig = "\""+GUIPrincipal.RUTA_ISE + "\\bin\\nt\\impact.exe\"" + " -batch \"" + GUIPrincipal.RUTA_PROYECTO_ACTUAL_SALIDAS + "\\cargaBit.cmd\"";
        Process proceso;
        
        //Comando de reconfiguración seguido del tamaño del bitstream parcial
        entsal.enviarBinaria(5);
        entsal.enviarBinaria(tam32str);
        try {
            status = entsal.recibirEntero();
            panel.append("Status icap inicialización: " + status+"\n");
            panel.append("**********************************************\n");
            if (status == 0) {
                //Esperar a que termine el scan() y recibir el número de bloques
                // del bitstream parcial
                //long tT, tf2, ti2 = 0;
                //Estadísticas
                tiempoInicial = System.currentTimeMillis();
                numeroConfErroneas=0;
                numeroConfErroneasEstaticas=0;
                numeroConf=0;
                BufferedWriter bw = new BufferedWriter(new FileWriter(GUIPrincipal.RUTA_PROYECTO_ACTUAL+"\\salidas\\reconfiguracion.txt",false));
                BufferedWriter bw_matlab = new BufferedWriter(new FileWriter(GUIPrincipal.RUTA_PROYECTO_ACTUAL+"\\salidas\\reconfiguracion_matlab.txt",false));
                entsal.esperarPalabraSyn();
                num_FDRI_blocks = entsal.recibirEntero();
                panel.append("FDRI blocks: "+num_FDRI_blocks+"\n\n");
                bw.write("FDRI blocks: "+num_FDRI_blocks+"\n\n");
                bw_matlab.write("FDRI blocks: "+num_FDRI_blocks+"\n\n");
                num_FDRI_block_address = 1;
                ///*     Para Estatico
                data_Str = Integer.toBinaryString(num_FDRI_block_address);
                entsal.enviarBinaria(data_Str);
                //*/
                for (int i = num_FDRI_block_address; i <= num_FDRI_blocks; i++) {
                    num_word = 0;
                    entsal.esperarPalabraSyn();
                    num_Words = entsal.recibirEntero();
                    ///*     Para Estatico
                    major_address = entsal.recibirEntero();
                    minor_address = entsal.recibirEntero();
                    entsal.esperarPalabraSyn();
                    data_Str = Integer.toBinaryString(num_word);
                    entsal.enviarBinaria(data_Str);
                    data_Str = Integer.toBinaryString(major_address);
                    entsal.enviarBinaria(data_Str);
                    data_Str = Integer.toBinaryString(minor_address);
                    entsal.enviarBinaria(data_Str);
                    //*/
                    word_address = 1;
                    ///*     Para Estatico
                    data_Str = Integer.toBinaryString(word_address);
                    entsal.enviarBinaria(data_Str);
                    //*/
                    bit_address = 0;
                    ///*     Para Estatico
                    data_Str = Integer.toBinaryString(bit_address);
                    entsal.enviarBinaria(data_Str);
                    //*/
                    panel.append("FDRI block "+ i +": Num words: "+ (num_Words - NUM_WORDS_FRAME) +"\n");
                    bw.write("FDRI block "+ i +": Num words: "+ (num_Words - NUM_WORDS_FRAME) +"\n");
                    bw_matlab.write("FDRI block "+ i +": Num words: "+ (num_Words - NUM_WORDS_FRAME) +"\n");
                    ///*     Para Estatico
                    max_major = generate_max_major(major_address,minor_address,num_Words);
                    //*/
                    //for (num_word = 1; num_word <= num_Words - NUM_WORDS_FRAME; num_word++) {
                        ///*     Para Estatico
                        for (major = major_address ; major < max_major ; major ++){
                            max_minor = generate_max_minor(major, num_word, num_Words);
                            for (minor = minor_address ; minor < max_minor ; minor ++){
                                minor_address = 0;
                                for (word = word_address ; word <= NUM_WORDS_FRAME ; word ++){
                                    num_word ++;
                                    for (bit = bit_address ; bit < NUM_BITS_WORD ; bit ++){
                        //*/
                        //for (bit = bit_address; bit < NUM_BITS_WORD; bit++) {
                            //Inyección de un error
                            numeroConf++;
                            if((numeroConf % 5000) == 0) 
                                panel.setText("");
                            if(entsal.esperarPalabraSyn())
                                {
                                //tf2 = System.currentTimeMillis();
                                //tT = (tf2 - ti2);
                                //System.out.print("\n Delay Reconfig : " + tT + " ms\n");
                                cicloError = entsal.recibirEntero();
                                //ti2 = System.currentTimeMillis();
                                }
                            else
                                cicloError = -1;
                            if (cicloError == 538976288)
                                {
                                numeroConf--;
                                bit = NUM_BITS_WORD;
                                panel.append("\n No Reconfiguración : Bloque "+i+", palabra "+num_word+", IOB Configuration Word.\n");
                                bw.write("\n No Reconfiguración : Bloque "+i+", palabra "+num_word+", IOB Configuration Word.\n");
                                }
                            else if (cicloError == 808464432)
                                {
                                numeroConf--;
                                bit = NUM_BITS_WORD;
                                panel.append("\n No Reconfiguración : Bloque "+i+", palabra "+num_word+", BRAM Configuration Word.\n");
                                bw.write("\n No Reconfiguración : Bloque "+i+", palabra "+num_word+", BRAM Configuration Word.\n");
                                }
                            else if (cicloError == 1077952576)
                                {
                                numeroConf--;
                                bit = NUM_BITS_WORD;
                                panel.append("\n No Reconfiguración : Bloque "+i+", palabra "+num_word+", CLK Configuration Word.\n");
                                bw.write("\n No Reconfiguración : Bloque "+i+", palabra "+num_word+", CLK Configuration Word.\n");
                                }
                            else if(cicloError == 269488144)
                                {
                                numeroConf--;
                                bit = NUM_BITS_WORD;
                                panel.append("\n No Reconfiguración : Bloque "+i+", palabra "+num_word+", HCLK Configuration Word.\n");
                                bw.write("\n No Reconfiguración : Bloque "+i+", palabra "+num_word+", HCLK Configuration Word.\n");
                                }
                            else 
                                {
                                if(cicloError != 0)
                                    {
                                    if(cicloError == -1)
                                        {
                                        numeroConfErroneasEstaticas++;
                                        panel.append("\nReconfiguración ("+status+"): Bloque "+i+", palabra "+num_word+", bit "+ bit +", error estatico en ciclo " + cicloError + ".\n");
                                        bw.write("\nReconfiguración ("+status+"): Bloque "+i+", palabra "+num_word+", bit "+ bit +", error estatico en ciclo " + cicloError + ".\n");
                                        bw_matlab.write(i + "\t" + num_word + "\t" + bit + "\t" + cicloError + "\n");
                                        ///*     Para Estatico
                                        tiempoInicial2 = System.currentTimeMillis();
                                        proceso = Runtime.getRuntime().exec(comando_reconfig);
                                        this.cargabit(proceso);
                                        proceso.waitFor();
                                        tiempoFinal2 = System.currentTimeMillis();
                                        long tiempoTotal = tiempoFinal2 - tiempoInicial2;
                                        tiempoTotal = tiempoTotal / 1000;
                                        if ( tiempoTotal2 < tiempoTotal)
                                            tiempoTotal2 = tiempoTotal;      
                                        while(!entsal.comprobarComunicacion());
                                        entsal.enviarBinaria(5);
                                        status = entsal.recibirEntero();
                                        entsal.esperarPalabraSyn();
                                        num_FDRI_blocks = entsal.recibirEntero();
                                        data_Str = Integer.toBinaryString(i);
                                        entsal.enviarBinaria(data_Str);
                                        entsal.esperarPalabraSyn();
                                        num_Words = entsal.recibirEntero();
                                        major_address = entsal.recibirEntero();
                                        minor_address = entsal.recibirEntero();                                     
                                        entsal.esperarPalabraSyn();
                                        data_Str = Integer.toBinaryString(num_word);
                                        entsal.enviarBinaria(data_Str);
                                        data_Str = Integer.toBinaryString(major);
                                        entsal.enviarBinaria(data_Str);
                                        data_Str = Integer.toBinaryString(minor);
                                        entsal.enviarBinaria(data_Str);
                                        data_Str = Integer.toBinaryString(word);
                                        entsal.enviarBinaria(data_Str);
                                        data_Str = Integer.toBinaryString(bit + 1);
                                        entsal.enviarBinaria(data_Str);
                                        //*/
                                        }
                                    else
                                        {
                                        numeroConfErroneas++;
                                        datoError = entsal.recibirBinaria(32);
                                        dato_Golden = entsal.recibirBinaria(32);
                                        panel.append("\nReconfiguración ("+status+"): Bloque "+i+", palabra "+num_word+", bit "+ bit +", error en ciclo " + cicloError + ".\n" + datoError+" != " + dato_Golden + "\n");
                                        bw.write("\nReconfiguración ("+status+"): Bloque "+i+", palabra "+num_word+", bit "+ bit +", error en ciclo " + cicloError + ".\n" + datoError+" != " + dato_Golden + "\n");
                                        bw_matlab.write(i + "\t" + num_word + "\t" + bit + "\t" + cicloError + "\n");
                                        /* PARA MOSTRAR LOS RESULTADOS DEL RESTORER
                                        datoError = ".";
                                        dato_Golden = ".";
                                        entsal.esperar_palabra_syn();
                                        cicloError = entsal.recibirEntero();
                                        if (cicloError != 0)
                                            {
                                            datoError = entsal.recibirBinaria(32);
                                            dato_Golden = entsal.recibirBinaria(32);
                                            }
                                        panel.append("\nReconfiguración (Restorer): Bloque "+i+", palabra "+num_word+", bit "+ bit +", error en ciclo " + cicloError + ".\n" + datoError+" != " + dato_Golden + "\n");
                                        bw.write("\nReconfiguración (Restorer): Bloque "+i+", palabra "+num_word+", bit "+ bit +", error en ciclo " + cicloError + ".\n" + datoError+" != " + dato_Golden + "\n");
                                        *     FIN DE MOSTRAR LOS RESULTADOS DEL RESTORER
                                        */
                                        }
                                    noErrores=0;
                                    }
                                else
                                    {
                                    panel.append("\nReconfiguración ("+status+"): Bloque "+i+", palabra "+num_word+", bit "+ bit +", sin error.\n");
                                    bw.write("\nReconfiguración ("+status+"): Bloque "+i+", palabra "+num_word+", bit "+ bit +", sin error.\n");
                                    bw_matlab.write(i + "\t" + num_word + "\t" + bit + "\t" + cicloError + "\n");
                                    noErrores++;
                                    /* PARA MOSTRAR LOS RESULTADOS DEL RESTORER
                                    datoError = ".";
                                    dato_Golden = ".";
                                    entsal.esperar_palabra_syn();
                                    cicloError = entsal.recibirEntero();
                                    if (cicloError != 0)
                                            {
                                            datoError = entsal.recibirBinaria(32);
                                            dato_Golden = entsal.recibirBinaria(32);
                                            }
                                    panel.append("\nReconfiguración (Restorer): Bloque "+i+", palabra "+num_word+", bit "+ bit +", error en ciclo " + cicloError + ".\n" + datoError+" != " + dato_Golden + "\n");
                                    bw.write("\nReconfiguración (Restorer): Bloque "+i+", palabra "+num_word+", bit "+ bit +", error en ciclo " + cicloError + ".\n" + datoError+" != " + dato_Golden + "\n");
                                        FIN DE MOSTRAR LOS RESULTADOS DEL RESTORER
                                    */
                                    //entsal.esperar_palabra_syn();
                                    //cicloError = entsal.recibirEntero();
                                    //panel.append("\nReconfiguración (RestorerCompleto): Bloque "+i+", palabra "+num_word+", bit "+ bit +", error en ciclo " + cicloError + ".\n");
                                    //bw.write("\nReconfiguración (RestorerCompleto): Bloque "+i+", palabra "+num_word+", bit "+ bit +", error en ciclo " + cicloError + ".\n");
                                    }
                                }
                            ///*     Para Estatico
                            }}
                            //*/
                        }
                    }
                }
                panel.append("\n**********************************************\n");
                bw.write("\n**********************************************\n");
                bw_matlab.write("\n**********************************************\n");
                //Estadísticas y resumen
                tiempoFinal = System.currentTimeMillis();
                mostrarResumen(tiempoFinal,tiempoInicial,numeroConf,numeroConfErroneas,numeroConfErroneasEstaticas,bw,bw_matlab);
                bw.close();
                bw_matlab.close();
                System.out.print("\n Delay Reconfig : " + tiempoTotal2 + " s\n");
            }else{
                panel.append("Error al inicializar el icap\n");
            }
        } catch (Exception ex) {
            Logger.getLogger(HiloInyeccion.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    private void cargabit(Process proc){
    try {
        InputStream is_std_1 = proc.getInputStream();
        InputStream is_err = proc.getErrorStream();
        InputStream is_std_2 = proc.getInputStream();
        BufferedReader br_std_1 = new BufferedReader(new InputStreamReader(is_std_1));
        BufferedReader br_err = new BufferedReader(new InputStreamReader(is_err));
        BufferedReader br_std_2 = new BufferedReader(new InputStreamReader(is_std_2));
        String s_std_1, s_err, s_std_2;
        s_std_1 = br_std_1.readLine();
        s_err = br_err.readLine();
        s_std_2 = br_std_2.readLine();
        while(s_std_1!=null && s_err!=null && s_std_2!=null)
            {
            s_std_1=br_std_1.readLine();
            s_err = br_err.readLine();
            s_std_2 = br_std_2.readLine();
            }
        } 
    catch (IOException ex)
        {
        Logger.getLogger(HiloSalidaStandarCargarBit.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    private int generate_max_major(int major_address, int minor_address, int num_words)
        {
	int num_frames = (num_words - NUM_WORDS_FRAME) / NUM_WORDS_FRAME;
	int max_major_address = major_address;
	if (minor_address != 0)
		{
		num_frames = num_frames - (minor_per_column[major_address] - minor_address);
		max_major_address ++;
		}
	while (num_frames > 0)
		{
		num_frames = num_frames - minor_per_column[major_address];
		max_major_address ++;
		}
	return max_major_address;
        }

    private int generate_max_minor(int major_address, int num_word, int num_words)
	{
	num_words = num_words - num_word;
	int num_frames = (num_words - NUM_WORDS_FRAME) / NUM_WORDS_FRAME;
	int max_minor_address = minor_per_column[major_address];
	if (num_frames < minor_per_column[major_address])
		max_minor_address = num_frames;
	return max_minor_address;
	}
    
    private void mostrarResumen(long tiempoFinal, long tiempoInicial, double numeroConf, double numeroConfErroneas, double numeroConfErroneasEstaticas, BufferedWriter bw, BufferedWriter bw_matlab) throws IOException{
        String resultados;
        String tiempoFormateado;
        long tiempoTotal;
        double porCientoErroneas;
        
        tiempoTotal = tiempoFinal - tiempoInicial;
        tiempoTotal = tiempoTotal / 1000; //Convertimos en segundos, se desprecia el resto
        int segundos = (int)tiempoTotal % 60 ;
        tiempoTotal = tiempoTotal / 60;
        int minutos = (int)tiempoTotal % 60;
        tiempoTotal = tiempoTotal / 60;
        int horas = (int)tiempoTotal % 24;
        int dias = (int)tiempoTotal / 24;
        tiempoFormateado = dias+"d:"+horas+"h:"+minutos+"m:"+segundos+"s";
        porCientoErroneas = ((double)(numeroConfErroneas + numeroConfErroneasEstaticas)/(double)numeroConf)*100;
        resultados = "\tTiempo tardado: \t\t\t"+tiempoFormateado+"\n"
                + "\tNúmero de reconfiguraciones: \t\t"+numeroConf+"\n"
                + "\tNúmero de reconfiguraciones erróneas: \t"+numeroConfErroneas+"\n"
                + "\tNúmero de reconfiguraciones estaticas erróneas: \t"+numeroConfErroneasEstaticas+"\n"
                + "\tPorcentaje de errores: \t\t\t"+porCientoErroneas+"%\n";
        panel.append(resultados);
        bw.write(resultados);
        bw_matlab.write(resultados);
        panel.setCaretPosition(panel.getText().length());
    }
    
}
     